﻿#ifndef ___PCLIB_S_SQLITE3___
#define ___PCLIB_S_SQLITE3___


#include "../m_core/m_core.hpp"


/*********************************************************************/
//可选功能：sqlite3数据库功能封装，支持加密和不加密数据库，用户可以通过定义SQLITE_HAS_CODEC来支持加密
/*********************************************************************/
#ifdef PCHAVE_SQLITE3
#if defined(QT_VERSION)
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#endif
extern "C"
{
#include "sqlite3.h"
};


/**
*@brief        表示一个字段，包含了字段名，字段值类型，字段值
*/
class CPCDBField
{
public:
    std::string name;           //字段名
    int         type;           //字段类型：SQLITE_NULL SQLITE_INTEGER SQLITE_FLOAT SQLITE_BLOB SQLITE_TEXT ..
    double      doublevalue;    //字段值：整数和浮点数
    std::string blobvalue;      //字段值：文本和blob

public:
    CPCDBField() :type(-1), doublevalue(0){}

    /**
    *@brief        清空
    */
    void Clear()
    {
        name.clear();
        type = -1;
        doublevalue = 0;
        blobvalue.clear();
    }

    /**
    *@brief     将字段值转换为字符串表示输出
    *@return    输出字符串。NULL和空字符串都返回空串。如果你必须区分这两种情况，可以用IsNULL()判断
    */
    std::string GetStringValue() const
    {
        char szBuff[32];
        switch (type)
        {
        case SQLITE_NULL:
            return "";
        case SQLITE_INTEGER:
            sprintf(szBuff, "%d", (int)doublevalue);
            return szBuff;
        case SQLITE_FLOAT:
            sprintf(szBuff, "%lf", doublevalue);
            return szBuff;
        case SQLITE_BLOB:
        case SQLITE_TEXT:
            return blobvalue;
        default:
            throw ("invalid type Can NOT ToString.");
        }
    }

    /**
    *@brief     将字段值转换为整数表示输出
    *@return    输出整数。如果无法转换，抛出异常。
    */
    int GetIntValue() const
    {
        switch (type)
        {
        case SQLITE_NULL:
            return 0;
        case SQLITE_INTEGER:
        case SQLITE_FLOAT:
            return (int)doublevalue;
        case SQLITE_BLOB:
        case SQLITE_TEXT:
            return atoi(blobvalue.c_str());
        default:
            throw ("invalid type Can NOT ToInt.");
        }
    }

    /**
    *@brief     将字段值转换为浮点表示输出
    *@return    输出浮点数。如果无法转换，抛出异常。
    */
    double GetDoubleValue() const
    {
        switch (type)
        {
        case SQLITE_NULL:
            return 0;
        case SQLITE_INTEGER:
        case SQLITE_FLOAT:
            return doublevalue;
        case SQLITE_BLOB:
        case SQLITE_TEXT:
            return atof(blobvalue.c_str());
        default:
            throw ("invalid type Can NOT ToDouble.");
        }
    }

#if defined(QT_VERSION)
    //qt接口
    QString GetQStringName() const
    {
        return QString::fromLocal8Bit(this->name.c_str());
    }
    QString GetQStringValue() const
    {
        return QString::fromLocal8Bit(this->GetStringValue().c_str());
    }
    QJsonValue GetQJsonValue(bool bUtf8 = true) const
    {
        switch (type)
        {
        case SQLITE_NULL:
            return QJsonValue(QJsonValue::Type::Null);
        case SQLITE_INTEGER:
            return QJsonValue((int)doublevalue);
        case SQLITE_FLOAT:
            return QJsonValue(doublevalue);
        case SQLITE_BLOB:
        case SQLITE_TEXT:
            if (bUtf8)
                return QJsonValue(QString::fromUtf8(this->blobvalue.c_str()));
            else
                return QJsonValue(QString::fromLocal8Bit(this->blobvalue.c_str()));
        default:
            return QJsonValue(QJsonValue::Type::Null);
        }
    }
#endif
public:
    bool IsNULL() const
    {
        return (type == SQLITE_NULL);
    }
    bool IsInt() const
    {
        return (type == SQLITE_INTEGER);
    }
    bool IsDouble() const
    {
        return (type == SQLITE_FLOAT);
    }
    bool IsString() const
    {
        return (type == SQLITE_TEXT);
    }
    bool IsBlob() const
    {
        return (type == SQLITE_BLOB);
    }
    bool IsValid() const
    {
        return (type == SQLITE_TEXT || type == SQLITE_INTEGER || type == SQLITE_FLOAT || type == SQLITE_BLOB || type == SQLITE_NULL);
    }
    std::string TypeString() const
    {
        switch (type)
        {
        case SQLITE_NULL:
            return "NULL";
        case SQLITE_INTEGER:
            return "INT";
        case SQLITE_FLOAT:
            return "FLOAT";
        case SQLITE_BLOB:
            return "BLOB";
        case SQLITE_TEXT:
            return "TEXT";
        default:
            return "INVALID";
        }
    }
};


/**
*@brief        表示一行记录
*/
class CPCDBRecord
{
public:
    std::vector< CPCDBField > fields;    //一行记录的所有字段
public:
    /**
    *@brief     这行记录是否包括了某个列
    *@param     name    [in]    列名
    *@param     field   [out]   如果包含了这个列，则输出这个列
    *@return    是否包括
    */
    bool contains(const std::string& name, CPCDBField& field) const
    {
        for (auto it = fields.begin(); it != fields.end(); ++it)
        {
            if ((*it).name == name)
            {
                field = (*it);
                return true;
            }
        }
        return false;
    }

    /**
    *@brief     返回可打印的一行记录
    *@return    字符串
    */
    std::string ToString() const
    {
        std::string ret;
        for (auto it = fields.cbegin(); it != fields.cend(); ++it)
        {
            const CPCDBField& field = *it;
            if (field.IsNULL())
                ret += "[NULL]";
            else
                ret += field.GetStringValue();
            ret += "\t";
        }
        return ret;
    }

    /**
    *@brief     返回可打印的表头
    *@return    字符串
    */
    std::string HeaderToString() const
    {
        std::string ret;
        for (auto it = fields.cbegin(); it != fields.cend(); ++it)
        {
            const CPCDBField& record = *it;
            ret += record.name;
            ret += "[";
            ret += record.TypeString();
            ret += "]";
            ret += "\t";
        }
        return ret;
    }

#if defined(QT_VERSION)
    bool contains(const QString& name, CPCDBField& field) const
    {
        return this->contains(name.toStdString(), field);
    }
#endif
};

/**
*@brief        表示所有记录的结果集
*/
class CPCDBResultSet
{
public:
    std::vector< CPCDBRecord > records;    //表示所有记录
public:
    /**
    *@brief     返回可打印的结果集
    *@return    字符串
    */
    std::string ToString() const
    {
        std::string ret;
        ret += "----------------------------------------------------------\n";
        if (!records.empty())
        {
            ret += records[0].HeaderToString();
            ret += "\n";
        }
        ret += "----------------------------------------------------------\n";
        for (auto it = records.cbegin(); it != records.cend(); ++it)
        {
            const CPCDBRecord& record = *it;
            ret += record.ToString();
            ret += "\n";
        }
        return ret;
    }
};

/**
*@brief        数据库对象类
*/
class CPCSQLite3DataBase : CPCNoCopyable
{
public:
    CPCSQLite3DataBase()
        :m_db(NULL)
        , m_isEncrypted(false)
        , m_isOpen(false)
    {
    }
    virtual ~CPCSQLite3DataBase()
    {
        this->Close();
    }

    /**
    *@brief     打开或创建数据库
    *@param     fileName    [in]    数据库文件路径。
    *@param     key         [in]    验证数据库密码。如果要将没有密码的数据库加密，最好使用ChangePassword函数
    *@param     flags       [in]    创建参数。
    *@return    是否成功
    */
    CPCResult<> Open(const std::string& fileName, const std::string& key = "", int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
    {
        CPCResult<> result;
        CPCFileUtil::PCCheckAndCreateDir(fileName.c_str());
        int rc = sqlite3_open_v2(fileName.c_str(), &m_db, flags, NULL);
        if (rc != SQLITE_OK)
        {
            return result.SetFail(rc,  "%s", sqlite3_errmsg(m_db));
        }
        rc = sqlite3_extended_result_codes(m_db, 1);
        if (rc != SQLITE_OK)
        {
            return result.SetFail(rc,  "%s", sqlite3_errmsg(m_db));
        }
#ifdef SQLITE_HAS_CODEC
        if (key.length() > 0)
        {
            rc = sqlite3_key(m_db, key.data(), (int)key.length());
            if (rc != SQLITE_OK)
            {
                return result.SetFail(rc, "%s", sqlite3_errmsg(m_db));
            }
            m_isEncrypted = true;
        }
#endif
        m_isOpen = true;
        return result.SetSucc();
    }

    /**
    *@brief     修改密码,调用这个接口之前，需要先调用open验证旧密码
    *            sqlite原生加密接口：
    *            验证密码：sqlite3_key
    *            清空密码：sqlite3_rekey(db,NULL,0);
    *            修改密码：sqlite3_rekey
    *
    *            如果1.db原来没有密码：
    *            1.调用sqlite3_rekey可以修改为有密码
    *            2.调用sqlite3_key也可以设置为有密码，但是只适应于第一次创建db文件且调用之后需要有创建表动作的情况。也就是如果调用sqlite3_key后直接关闭数据库，这个db文件并不会设置为有密码；但是如果sqlite3_key后再创建一个表，这个情况下db文件就为有密码了。
    *
    *            如果1.db原来有密码：
    *            1.直接调用sqlite3_rekey会失败，因为没有先验证密码
    *            2.如果直接调用清空密码：sqlite3_rekey(db,NULL,0);会成功，但是实际并没有清空掉密码
    *@param     newkey        [in]    数据库新密码。如果newkey为""，则表示移除密码。
    *@return    是否成功
    */
    CPCResult<> ChangePassword(const std::string& newkey)
    {
        CPCResult<> result;
#ifdef SQLITE_HAS_CODEC
        if (!this->IsOpen())
        {
            return result.SetFail("data base not open.");
        }
        int rc = 0;
        if (newkey.empty())
        {
            rc = sqlite3_rekey(m_db, NULL, 0);
        }
        else
        {
            rc = sqlite3_rekey(m_db, newkey.data(), (int)newkey.length());
        }
        if (rc != SQLITE_OK)
        {
            return result.SetFail(rc, "%s", sqlite3_errmsg(m_db));
        }
#endif
        return result;
    }

    /**
    *@brief        关闭数据库
    */
    void Close()
    {
        m_isEncrypted = false;
        m_isOpen = false;
        if (m_db != NULL)
        {
            sqlite3_close(m_db);
            m_db = NULL;
        }
    }

    /**
    *@brief        数据库是否打开
    */
    bool IsOpen() const
    {
        return (m_db != NULL && m_isOpen);
    }

    /**
    *@brief        检查sql语法
    */
    bool CheckSyntax(const char* sql) const
    {
        return sqlite3_complete(sql) != 0;
    }

    /**
    *@brief        检查是否为自动提交
    */
    bool GetAutoCommit() const
    {
        return sqlite3_get_autocommit(m_db) != 0;
    }

    /**
    *@brief        设置保存点
    *@param        savepointName        [in]    保存点，名字不要包含引号
    */
    CPCResult<int> Savepoint(const char* savepointName)
    {
        std::string sql = "savepoint \"";
        sql += savepointName;
        sql += "\"";
        return ExecuteUpdate(sql);
    }

    /**
    *@brief     开始事务
    *@param     transactionType        [in]    事务类型，默认为普通事务。transactionType可选：
    "deferred"
    "immediate"
    "exclusive"
    *@return    是否成功
    */
    CPCResult<int> Begin(const char* transactionType = "")
    {
        std::string sql = "begin ";
        sql += transactionType;
        sql += " transaction";
        return ExecuteUpdate(sql);
    }

    /**
    *@brief        提交事务
    */
    CPCResult<int> Commit()
    {
        auto result = ExecuteUpdate("commit transaction");
        if (!result)
        {
            return Rollback();
        }
        return result;
    }

    /**
    *@brief        回滚事务
    *@param        savepointName        [in]    保存点，名字不要包含引号
    */
    CPCResult<int> Rollback(const char* savepointName = "")
    {
        if (savepointName.empty())
        {
            return ExecuteUpdate("rollback transaction");
        }
        else
        {
            std::string sql = "rollback transaction to savepoint \"";
            sql += savepointName;
            sql += "\"";
            return ExecuteUpdate(sql);
        }
    }

    /**
    *@brief     执行sql语句
    *@param     sql      [in]    sql语句
    *@return    是否成功。如果成功，int为修改的行数量
    */
    CPCResult<int> ExecuteUpdate(const char* sql)
    {
        CPCGuard<CPCRecMutex> guard(m_DbMutex);
        CPCResult<int> result;
        if (!this->IsOpen())
        {
            return result.SetFail("data base not open.");
        }
        char* localError = 0;
        int rc = sqlite3_exec(m_db, sql, 0, 0, &localError);
        if (rc == SQLITE_OK)
        {
            return result.SetSucc(sqlite3_changes(m_db));
        }
        else
        {
            result.SetFail(rc,"%s",localError);
            sqlite3_free(localError);
            return result;
        }
    }

    /**
    *@brief     执行查询sql语句,查找所有结果
    *@param     sql          [in]    sql语句
    *@param     resultset    [out]   查询结果集
    *@return    是否成功。
    */
    CPCResult<> ExecuteQueryAll(const char* sql, CPCDBResultSet &resultset)
    {
        CPCGuard<CPCRecMutex> guard(m_DbMutex);
        resultset.records.clear();

        CPCResult<> result;
        if (!this->IsOpen())
        {
            return result.SetFail("data base not open.");
        }

        sqlite3_stmt* stmt = 0;
        int rc = sqlite3_prepare_v2(m_db, sql, -1, &stmt, 0);
        if (rc != SQLITE_OK)
        {
            return result.SetFail(rc, sqlite3_errmsg(m_db));
        }
        int ncolumn = sqlite3_column_count(stmt);
        if (ncolumn < 0)
        {
            return result.SetFail("result is invalid. ncolumn = %d", ncolumn);
        }
        if (ncolumn == 0)
        {
            sqlite3_finalize(stmt);
            return result;
        }

        while (1)
        {
            rc = sqlite3_step(stmt);
            if (rc == SQLITE_DONE)
            {
                break;
            }
            else if (rc == SQLITE_ROW)
            {
                CPCDBRecord record;
                for (int i = 0; i < ncolumn; i++)
                {
                    CPCDBField field;
                    field.name = sqlite3_column_name(stmt, i);
                    field.type = sqlite3_column_type(stmt, i);
                    switch (field.type)
                    {
                    case SQLITE_NULL:
                        field.doublevalue = 0;
                        break;
                    case SQLITE_INTEGER:
                        field.doublevalue = sqlite3_column_int(stmt, i);
                        break;
                    case SQLITE_FLOAT:
                        field.doublevalue = sqlite3_column_double(stmt, i);
                        break;
                    case SQLITE_BLOB:
                        field.blobvalue.assign((const char*)sqlite3_column_blob(stmt, i), sqlite3_column_bytes(stmt, i));
                        break;
                    case SQLITE_TEXT:
                        field.blobvalue = (char*)sqlite3_column_text(stmt, i);
                        break;
                    default:
                        break;
                    }
                    record.fields.push_back(field);
                }
                resultset.records.push_back(record);
            }
            else
            {
                sqlite3_finalize(stmt);
                return result.SetFail(rc, sqlite3_errmsg(m_db));
            }
        }
        sqlite3_finalize(stmt);
        return result;
    }

#if defined(QT_VERSION)
    //qt接口
    CPCResult<> Open(const QString& fileName, const QString& key = "", int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
    {
        return this->Open(fileName.toStdString(), key.toStdString(), flags);
    }
    CPCResult<> ChangePassword(const QString& newkey)
    {
        return this->ChangePassword(newkey.toStdString());
    }
    CPCResult<int> ExecuteUpdate(const QString& sql)
    {
        return this->ExecuteUpdate(sql.toStdString());
    }
    CPCResult<> ExecuteQueryAll(const QString& sql, CPCDBResultSet &resultset)
    {
        return this->ExecuteQueryAll(sql.toStdString(), resultset);
    }
#endif
protected:
    sqlite3*    m_db;
    bool        m_isEncrypted;
    bool        m_isOpen;
    CPCRecMutex m_DbMutex;
};


#endif //PCHAVE_SQLITE3 


#endif    //___PCLIB_S_SQLITE3___